/*
 * CalendarFrame.java
 */

package gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.GregorianCalendar;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.UIManager;

import exceptions.UPException;

import model.Job;
import model.ListManager;

/**
 * CalendarFrame is a robust JFrame that displays
 * a calendar with clickable jobs from the Urban Parks program
 * shown in the date panes.
 * 
 * @author Sean Brown
 * @version 1.0
 */
@SuppressWarnings("serial")
public class CalendarFrame extends JFrame { 
	
	/**
	 * Array of the names of the week.
	 */
	private static final String[] DAYS_OF_WEEK = new String[]{			
			"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
			"Friday", "Saturday"};
	
	/**
	 * Array for the names of the months.
	 */
	private static final String[] MONTHS = new String[]{
			"January","February","March","April","May","June",
			"July","August","September","October","November",
			"December"};
	
	/**
	 * Default window size.
	 */
	private static final Dimension WINDOW_SIZE = new Dimension(800, 600);
	
	/**
	 * The color black.
	 */
	private static final Color BG_COLOR = Color.black;
	
	/**
	 * The key to associate jobs with their id values.
	 */
	private static final Object KEY = new Object();
	
	/**
	 * The WelcomeFrame that opened this CalendarFrame.
	 */
	private final WelcomeFrame my_welcome_frame;
	
	/**
	 * The JMenuBar for this JFrame.
	 */
	private final CalendarMenuBar my_cmb;
	
	/**
	 * The ListManager for this instance of the program.
	 */
	private final ListManager my_lm;
	
	/**
	 * The JPanel that shows the actual calendar itself.
	 */
	private JPanel my_cal_panel;
	
	/**
	 * The JPanel that shows the name of the month.
	 */
	private JPanel my_month_panel;
	
	/**
	 * The current date/time.
	 */
	private GregorianCalendar my_current_date;
	
	/**
	 * Constructor instantiates the JFrame and associates to
	 * the parent window and ListManager.
	 * 
	 * @param the_parent The WelcomeFrame that created this frame.
	 * @param the_lm The ListManager to use to access data.
	 */
	public CalendarFrame(final WelcomeFrame the_parent, final ListManager the_lm) {
		super("Urban Parks: Calendar");
		my_lm = the_lm;
		my_cmb = new CalendarMenuBar(the_parent, this, my_lm);
		my_welcome_frame = the_parent;
	}
	
	/**
	 * Sets up all panels, preferences, and shows the frame
	 * on screen.
	 */
	public void start() {
		setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		 
		//create custom close operation
		addWindowListener(new WindowAdapter() {
		public void windowClosing(WindowEvent e) {
			exit();
		}});
		
		UIManager.put("Panel.background", Color.WHITE);
		setJMenuBar(my_cmb);
		my_current_date = new GregorianCalendar();

		final JPanel my_main_layout = new JPanel(new BorderLayout());
		
		final JPanel nav_layout = new JPanel(new BorderLayout());
		nav_layout.add(welcomePanel(), BorderLayout.NORTH);
		nav_layout.add(prevPanel(), BorderLayout.WEST);
		nav_layout.add(nextPanel(), BorderLayout.EAST);
		my_month_panel = new JPanel(new BorderLayout());
		drawMonthPanel();
		nav_layout.add(my_month_panel, BorderLayout.CENTER);
		
		final JPanel my_cal_layout = new JPanel(new BorderLayout());
		my_cal_layout.add(headerPanel(), BorderLayout.NORTH);
		my_cal_panel = new JPanel(new BorderLayout());
		drawCalendarPanel();
		my_cal_layout.add(my_cal_panel, BorderLayout.CENTER);
		
		my_main_layout.add(nav_layout, BorderLayout.NORTH);
		my_main_layout.add(my_cal_layout, BorderLayout.CENTER);
		
		add(my_main_layout);
		
		setPreferredSize(WINDOW_SIZE);
		pack();
		setLocationRelativeTo(getRootPane());
		setVisible(true);
	}
	
	public void exit() {
		my_welcome_frame.exitProcedure(this);
	}
	
	/**
	 * Changes the program to display the month forward or backwards
	 * by the amount in the parameter the_change.
	 * 
	 * @assumes for this program the_change is 1 or -1.
	 * @param the_change How many months to advance/reverse.
	 */
	private void changeMonth (final int the_change) {
		my_current_date.add(GregorianCalendar.MONTH, the_change);
		drawCalendarPanel();
        drawMonthPanel();
		validate();
	}

	/**
	 * Draws (or redraws) the JPanel that shows the name of the month/year.
	 */
	private void drawMonthPanel() {
		my_month_panel.removeAll();
		final JPanel header_panel = new JPanel(new BorderLayout());
		final JLabel month = new JLabel(MONTHS[my_current_date.get
		                                       (GregorianCalendar.MONTH)] + 
		                                       ", " + my_current_date.get
		                                       (GregorianCalendar.YEAR), 
		                                       JLabel.CENTER);
		month.setFont(GUIUtil.largerFont(month));
		header_panel.add(month);
		my_month_panel.add(header_panel, BorderLayout.CENTER);
	}

	/**
	 * Returns a JPanel with the button for switching to the 
	 * next month.
	 * 
	 * @return JPanel with the next button.
	 */
	private JPanel nextPanel() {
		final JPanel next_panel = new JPanel(new FlowLayout());
		final JButton next_button = new JButton(">>");
		next_button.setBackground(Color.WHITE);
		next_button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e)
            {
                changeMonth(1);
            }
        });
		next_button.setFont(GUIUtil.largerFont(next_button));
		next_panel.add(next_button);
		return next_panel;
	}

	/**
	 * Returns a JPanel with the button for switching to the 
	 * previous month.
	 * 
	 * @return JPanel with the previous button.
	 */
	private JPanel prevPanel() {
		final JPanel prev_panel = new JPanel(new FlowLayout());
		final JButton prev_button = new JButton("<<");
		prev_button.setBackground(Color.WHITE);
		prev_button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e)
            {
                changeMonth(-1);
            }
        });
		prev_button.setFont(GUIUtil.largerFont(prev_button));
		prev_panel.add(prev_button);
		return prev_panel;
	}

	/**
	 * Draws (or redraws) the calendar panel in it's current selected
	 * state.
	 */
	private void drawCalendarPanel() {
		my_cal_panel.removeAll();
		final GregorianCalendar start_date = (GregorianCalendar) my_current_date.clone();
		//Get to the first of the month
		while (start_date.get(GregorianCalendar.DAY_OF_MONTH)!= 1) {
			start_date.add(GregorianCalendar.DATE, -1);
		}

		//Get to the first Sunday of the month
		final int day_of_week = start_date.get(GregorianCalendar.DAY_OF_WEEK);
		if (day_of_week != 1) {
			start_date.add(GregorianCalendar.DATE, -(day_of_week - 1));
		}
		
		int num_days = DAYS_OF_WEEK.length;
		int rows = (int)Math.ceil(((double)my_current_date.getActualMaximum
				(GregorianCalendar.DAY_OF_MONTH) + (double)(day_of_week - 1)) / 
				(double)num_days);
		final JPanel month_panel = new JPanel(new GridLayout(rows, num_days, 1, 1));
		month_panel.setBackground(BG_COLOR);
		
		//Add panels for each day box
		boolean print_in_gray = true;
		for (int i = 0; i < rows * num_days; i++) {
			final int current_day = start_date.get(GregorianCalendar.DAY_OF_MONTH);

			if (current_day == 1) {
				print_in_gray = !print_in_gray;
			}
			final JPanel entire_panel = new JPanel(new BorderLayout());
			final JPanel day_panel = new JPanel();
			day_panel.setLayout(new BoxLayout(day_panel, BoxLayout.X_AXIS));
			day_panel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
			final JLabel day = new JLabel("" + current_day);
			if (print_in_gray) {
				day.setForeground(Color.LIGHT_GRAY);
			}
			day_panel.add(day);
			entire_panel.add(day_panel, BorderLayout.NORTH);
			final List<Job> jobs_list = 
					my_lm.getJobsOn((GregorianCalendar)start_date.clone());
			if (!jobs_list.isEmpty()){
				entire_panel.add(smallJobPanel(jobs_list), 
						BorderLayout.CENTER);
			}
			month_panel.add(entire_panel);
			start_date.add(GregorianCalendar.DATE, 1);
		}
		my_cal_panel.add(month_panel, BorderLayout.CENTER);
	}
	
	/**
	 * Returns a JPanel with the days of the week listed in a grid.
	 * 
	 * @return JPanel to display the days of the week above the calendar.
	 */
	private JPanel headerPanel() {
		final JPanel header_panel = new JPanel(new GridLayout(1, DAYS_OF_WEEK.length, 1, 1));
		header_panel.setBackground(BG_COLOR);
		for (String s : DAYS_OF_WEEK) {
			final JLabel day_of_week = new JLabel(s.substring(0, 3), JLabel.CENTER);
			day_of_week.setForeground(Color.WHITE);
			header_panel.add(day_of_week);
		}
		return header_panel;
	}
	
	/**
	 * Returns a JPanel containing a listing for each Job passed.
	 * 
	 * @param the_jobs The Jobs to display listing for.
	 * @return JPanel with the jobs listed (and links).
	 */
	private JPanel smallJobPanel(final List<Job> the_jobs) {
		final JPanel jobs_panel = new JPanel();
		jobs_panel.setLayout(new BoxLayout(jobs_panel, BoxLayout.PAGE_AXIS));
		for (Job j : the_jobs) {
			final JTextArea job_area = new JTextArea();
			try {
				job_area.append(my_lm.findPark(j.getParkId()).getName());
				job_area.append(": ");
				job_area.append(Job.timeToString(j.getStartTime()));
			} catch (final UPException e) {
				System.err.println(e.getMessage());
			}
			job_area.setCursor(new Cursor(Cursor.HAND_CURSOR));
			job_area.setFont(GUIUtil.smallerFont(job_area));
			job_area.getCaret().setVisible(false);
			job_area.setEditable(false);
			job_area.addMouseListener(new MouseListener() {
				public void mouseClicked(final MouseEvent the_event)
	            {
					JTextArea job = (JTextArea) the_event.getSource();
	    			int job_id = (Integer) job.getClientProperty(KEY);
	    			final JobFrame jf = new JobFrame(my_lm, job_id);
	    			jf.start();
	            }
				//None of these are used.
				@Override
				public void mouseEntered(final MouseEvent the_event) {
				}
				@Override
				public void mouseExited(final MouseEvent the_event) {
				}
				@Override
				public void mousePressed(final MouseEvent the_event) {
				}
				@Override
				public void mouseReleased(final MouseEvent the_event) {
				}
	        });
			job_area.putClientProperty(KEY, j.getID());
			jobs_panel.add(job_area);
		}
		return jobs_panel;
	}
	
	/**
	 * Returns a JPanel displaying a welcome message to the currently
	 * logged in user.
	 * 
	 * @return JPanel to display.
	 */
	private JPanel welcomePanel() {
		JPanel result = new JPanel();
		JLabel user_label = new JLabel("Welcome " + my_lm.getCurrentUser().getName() + "!", JLabel.LEFT);
		user_label .setFont(GUIUtil.largerFont(result));
		user_label .setHorizontalAlignment(JLabel.LEFT);					
		result.add(user_label);
		return result;
	}
}
